/*
Copyright (C) 2008  YaoKun(ykxss@163.com)

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
    
    Notice the Additional Terms please.
*/
package p2proxy;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.InterruptedIOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.URI;
import java.net.URISyntaxException;

import net.jxta.document.AdvertisementFactory;
import net.jxta.exception.PeerGroupException;
import net.jxta.id.IDFactory;
import net.jxta.peergroup.PeerGroup;
import net.jxta.pipe.PipeID;
import net.jxta.pipe.PipeService;
import net.jxta.platform.NetworkManager;
import net.jxta.protocol.PipeAdvertisement;
import net.jxta.socket.JxtaSocket;

public class Client {

	private transient PeerGroup netPeerGroup = null;
	private transient PipeAdvertisement pipeAdv;
	public final static String SOCKETIDSTR = "urn:jxta:uuid-59616261646162614E5047205032503393B5C2F6CA7A41FBB0F890173088E79404";
	ServerSocket ssocket;//ServerSocket is to listen for a connection and block the port 8080
	JxtaSocket jsocket;//JxtaSocket which is send and receive message from p2p network
	Socket socket;//When ServerSocket accept() return, it take the resulting Socket and use it to handle message
	int jo,ji;
	byte[] bjo = new byte[1024];
	byte[] bji = new byte[1024];
	static public int TIMEOUT = 50;
	
	/**
	* 启动客户端的代理程序.
	* <p>线程启动本程序，准备调用相关服务的启动方法
	* @author YaoKun(ykxss@163.com)
	*/
	public static void main(String args[]) {
		Client c = new Client();
		try {
			c.client();
		} catch (PeerGroupException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	/**
	* 进行初始化.
	* <p>初始化节点，准备调用连接服务的启动方法
	* @throws IOException,PeerGroupException
	* @return 没有返回值
	* @author YaoKun(ykxss@163.com)
	*/
	public void client() throws IOException, PeerGroupException {
		NetworkManager manager = new NetworkManager(
				NetworkManager.ConfigMode.ADHOC, "client", new File(new File(".cache"), "client").toURI());
		manager.startNetwork();
		netPeerGroup = manager.getNetPeerGroup();
		pipeAdv = createSocketAdvertisement();
		connect();
	}
	
	/**
	* 创建节点公告.
	* <p>这里创建的节点公告和服务端的节点公告是一直的，只有这样才可以找到对方。是使用同一个PipeID来实现的。
	* @return PipeAdvertisement
	* @exception URISyntaxException
	* @author YaoKun(ykxss@163.com)
	*/
	private static PipeAdvertisement createSocketAdvertisement() {
		PipeID socketID = null;

		try {
			socketID = (PipeID) IDFactory.fromURI(new URI(SOCKETIDSTR));
		} catch (URISyntaxException use) {
			use.printStackTrace();
		}
		PipeAdvertisement advertisement = (PipeAdvertisement) AdvertisementFactory
				.newAdvertisement(PipeAdvertisement.getAdvertisementType());

		advertisement.setPipeID(socketID);
		advertisement.setType(PipeService.UnicastType);
		advertisement.setName("Socket tutorial");
		return advertisement;
	}
	
	/**
	* 创建连接的方法.
	* <p>创建节点建监听对象监听来自浏览器的数据。一旦接收到连接，就创建一个线程来处理接收的数据。
	* @return 没有返回值
	* @exception Exception，IOException
	* @author YaoKun(ykxss@163.com)
	*/
	public void connect() {
		try {
			ssocket = new ServerSocket(8080);
			try {
				while (true) {
					try {
						socket = ssocket.accept();
						socket.setSoTimeout(TIMEOUT);
						if (socket != null) {
							Thread thread = new Thread(new ClientConnectionHandler(),"Client Connection Handler Thread");
							thread.start();
						}
					} catch (Exception e) {
//						e.printStackTrace();
					}
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		} catch (IOException e) {
		}

	}
	
	/**
	 * 一个专门处理客户端数据的内部类.
	 * <p>这是个线程类。接受到数据后就对数据进行处理，包括输入输出数据的处理。等全部数据处理完成后线程结束。
	 * @author YaoKun(ykxss@163.com)
	 */
	class ClientConnectionHandler implements Runnable {
		InputStream jin, sin;
		OutputStream jout, sout;
		
		/**
		 * 构造函数.
		 * <p>首先创建连接服务端的socket，然后接受来自浏览器的socket请求，准备好两个socket的输入输出流。
		 * @param ssocket 这是来自客户端的socket
		 * @return 没有返回值
		 * @exception IOException
		 * @author YaoKun(ykxss@163.com)
		 */
		ClientConnectionHandler() {
			try {
				jsocket = new JxtaSocket(netPeerGroup,// no specific peerid
						null, pipeAdv,// connection timeout: 5 seconds
						5000,// reliable connection
						true);
				jsocket.setSoTimeout(TIMEOUT);
			} catch (IOException e) {
				e.printStackTrace();
				System.exit(-1);
			}
			
			try {
				sin = socket.getInputStream();
				sout = socket.getOutputStream();
				jin = jsocket.getInputStream();
				jout = jsocket.getOutputStream();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		
		/**
		 * 内部类线程启动函数.
		 * <p>调用处理数据的方法
		 * @return 没有返回值
		 * @author YaoKun(ykxss@163.com)
		 */
		public void run() {
			sendAndReceiveData();
		}
		
		/**
		 * 数据处理的方法.
		 * <p>这个方法主要是对两个socket的输入输出流进行处理。将浏览器的数据写入客户端的socket的输出流，然后将服务端返回的数据写入到web服务器的socket的输入流
		 * @param sin 到web服务器的socket的输入流
		 * @param jin 客户端的socket的输入流
		 * @param sout 到web服务器的socket的输出流
		 * @param jout 客户端的socket的输出流
		 * @return 没有返回值
		 * @exception IOException,Exception
		 * @author YaoKun(ykxss@163.com)
		 */
		private void sendAndReceiveData() {

			try {
				int ir=0;
				byte bytes[] = new byte[1024];
				while (true) {
					try {System.out.println("while1="+ir);
						if ((ir = sin.read(bytes))!=-1) {System.out.println(ir+"``````````````11111111111111");
							jout.write(bytes, 0, ir);
							jout.flush();
							System.out.println(ir+"````````````222222222222222222");
						} else if (ir==-1){
							System.out.println(ir+"`````````````33333333333333333333");
							break;
						}
							
					} catch (InterruptedIOException e) {
						System.out.println("exception1");
						e.printStackTrace();
					}
					try {System.out.println("while2="+ir);
						if ((ir = jin.read(bytes))!=-1) {System.out.println(ir+"~~~~~~~~~~~~~~~~~11111111111111");
							sout.write(bytes, 0, ir);
							sout.flush();
							System.out.println(ir+"~~~~~~~~~~~~~~~~~2222222222222222222");
						} else if (ir==-1){
							System.out.println(ir+"~~~~~~~~~~~~~~~~~~~~~~~~~333333333333333333333333");
							break;
						}
							
					} catch (InterruptedIOException e) {
						System.out.println("exception2");
						e.printStackTrace();
					}
				}
			} catch (Exception e0) {
				System.out.println("Pipe exception: " + e0);
			}finally{
				try {
					sin.close();System.out.println("copyofslient sin1.close()");
					jin.close();System.out.println("copyofslient jin1.close()");
					sout.close();System.out.println("copyofslient sout1.close()");
					jout.close();System.out.println("copyofslient jout1.close()");
					socket.close();
					ssocket.close();
					jsocket.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			
			
		}
	}
}